<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="VE0cT" id="VE0cT"><span data-lake-id="u5abfd597" id="u5abfd597">典型回答</span></h1>
  <p data-lake-id="u226a4f45" id="u226a4f45"><span data-lake-id="ue1abc05f" id="ue1abc05f">根据Java虚拟机规范的定义，JVM的运行时内存区域主要由Java堆、虚拟机栈、本地方法栈、方法区和程序计数器以及运行时常量池组成。其中堆、方法区以及运行时常量池是线程之间共享的区域，而栈（本地方法栈+虚拟机栈）、程序计数器都是线程独享的。</span></p>
  <p data-lake-id="u3414e219" id="u3414e219"><span data-lake-id="u4484ab12" id="u4484ab12">​</span><br></p>
  <p data-lake-id="u420df6f2" id="u420df6f2"><span data-lake-id="uaa752950" id="uaa752950">需要注意的是，上面的这6个区域，是虚拟机规范中定义的，但是在具体的实现上，不同的虚拟机，甚至是同一个虚拟机的不同版本，在实现细节上也是有区别的。</span></p>
  <p data-lake-id="ua52e5e13" id="ua52e5e13"><span data-lake-id="u911abedf" id="u911abedf">​</span><br></p>
  <p data-lake-id="u9804bde1" id="u9804bde1"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1694329068288-9e81f4b6-9f65-431c-aa3d-d2b58c889829.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_24%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="ua2aaa74b" id="ua2aaa74b"><span data-lake-id="ud884173d" id="ud884173d">​</span><br></p>
  <p data-lake-id="u9ffe3a2e" id="u9ffe3a2e"><strong><span data-lake-id="udb47a3d1" id="udb47a3d1">程序计数器</span></strong><span data-lake-id="uc76ec41e" id="uc76ec41e">：一个只读的存储器，用于记录Java虚拟机正在执行的字节码指令的地址。它是线程私有的，为每个线程维护一个独立的程序计数器，用于指示下一条将要被执行的字节码指令的位置。它保证线程执行一个字节码指令以后，才会去执行下一个字节码指令。</span></p>
  <p data-lake-id="u77077f10" id="u77077f10"><span data-lake-id="u2e09d294" id="u2e09d294">​</span><br></p>
  <p data-lake-id="u0e048d3a" id="u0e048d3a"><strong><span data-lake-id="uc3ff8b4c" id="uc3ff8b4c">Java虚拟机栈</span></strong><span data-lake-id="ue00e9d06" id="ue00e9d06">：一种线程私有的存储器，用于存储Java中的局部变量。根据Java虚拟机规范，每次方法调用都会创建一个栈帧，该栈帧用于存储局部变量，操作数栈，动态链接，方法出口等信息。当方法执行完毕之后，这个栈帧就会被弹出，变量作用域就会结束，数据就会从栈中消失。</span></p>
  <p data-lake-id="u5e957fb6" id="u5e957fb6"><span data-lake-id="ue2a4cc44" id="ue2a4cc44">​</span><br></p>
  <p data-lake-id="uacb515ce" id="uacb515ce"><strong><span data-lake-id="u772e7c9b" id="u772e7c9b">本地方法栈</span></strong><span data-lake-id="u69ae940c" id="u69ae940c">：本地方法栈是一种特殊的栈，它与Java虚拟机栈有着相同的功能，但是它支持本地代码（ Native Code ）的执行。本地方法栈中存放本地方法（ Native Method ）的参数和局部变量，以及其他一些附加信息。这些本地方法一般是用C等本地语言实现的，虚拟机在执行这些方法时就会通过本地方法栈来调用这些本地方法。</span></p>
  <p data-lake-id="ud48a118b" id="ud48a118b"><span data-lake-id="u1525843d" id="u1525843d">​</span><br></p>
  <p data-lake-id="u202c8f84" id="u202c8f84"><strong><span data-lake-id="uda47b988" id="uda47b988">Java堆</span></strong><span data-lake-id="u7cc81375" id="u7cc81375">：是存储对象实例的运行时内存区域。它是虚拟机运行时的内存总体的最大的一块，也一直占据着虚拟机内存总量的一大部分。Java堆由Java虚拟机管理，用于存放对象实例，是几乎所有的对象实例都要在上面分配内存。此外，Java堆还用于垃圾回收，虚拟机发现没有被引用的对象时，就会对堆中对象进行垃圾回收，以释放内存空间。</span></p>
  <p data-lake-id="uf45a5457" id="uf45a5457"><span data-lake-id="u7b9db265" id="u7b9db265">​</span><br></p>
  <p data-lake-id="u1e2ae3be" id="u1e2ae3be"><strong><span data-lake-id="u44786c78" id="u44786c78">方法区</span></strong><span data-lake-id="u5419dc03" id="u5419dc03">：用于存储已被加载的类信息、常量、静态变量、即时编译后的代码等数据的内存区域。每加载一个类，方法区就会分配一定的内存空间，用于存储该类的相关信息，这部分空间随着需要而动态变化。方法区的具体实现形式可以有多种，比如堆、永久代、元空间等。</span></p>
  <p data-lake-id="u103ad25e" id="u103ad25e"><span data-lake-id="ue7059fe0" id="ue7059fe0">​</span><br></p>
  <p data-lake-id="u7e587ce3" id="u7e587ce3"><strong><span data-lake-id="ucc8f025d" id="ucc8f025d">运行时常量池</span></strong><span data-lake-id="ua0e29657" id="ua0e29657">：是方法区的一部分。用于存储编译阶段生成的信息，主要有</span><strong><span data-lake-id="uf747efbf" id="uf747efbf">字面量和符号引用常量</span></strong><span data-lake-id="ue68eac04" id="ue68eac04">两类。其中符号引用常量包括了类的全限定名称、字段的名称和描述符、方法的名称和描述符。</span></p>
  <p data-lake-id="u2da4ed1e" id="u2da4ed1e"><span data-lake-id="u2786e6ed" id="u2786e6ed">​</span><br></p>
  <p data-lake-id="u14d11afd" id="u14d11afd"><br></p>
  <h1 data-lake-id="BDw4F" id="BDw4F"><span data-lake-id="ub84771e3" id="ub84771e3">扩展知识</span></h1>
  <p data-lake-id="uc7258674" id="uc7258674"><br></p>
  <h2 data-lake-id="Tvak5" id="Tvak5"><span data-lake-id="ua2b07ab3" id="ua2b07ab3">堆和栈的区别</span></h2>
  <p data-lake-id="u83ec83da" id="u83ec83da"><br></p>
  <p data-lake-id="u57321c2c" id="u57321c2c"><span data-lake-id="u9901f98f" id="u9901f98f">堆和栈是Java程序运行过程中主要存储区域，经常被拿来对比，他们主要有以下区别（这里的栈主要指的是虚拟机栈）：</span></p>
  <p data-lake-id="u3a6ab74d" id="u3a6ab74d"><br></p>
  <p data-lake-id="ufd252090" id="ufd252090"><span data-lake-id="u1c958582" id="u1c958582">1、</span><strong><span data-lake-id="ub66b1dc3" id="ub66b1dc3">存储位置不同</span></strong><span data-lake-id="u8eebce1f" id="u8eebce1f">，堆是在JVM堆内存中分配空间，而栈是在JVM的栈内存中分配空间。</span></p>
  <p data-lake-id="ub7203f15" id="ub7203f15"><span data-lake-id="u04c9bfca" id="u04c9bfca">2、</span><strong><span data-lake-id="u6dbf4fdc" id="u6dbf4fdc">存储的内容不同</span></strong><span data-lake-id="uc881e218" id="uc881e218">，堆中主要存储对象，栈中主要存储本地变量</span></p>
  <p data-lake-id="u1c7e792a" id="u1c7e792a"><span data-lake-id="udfd8ba8c" id="udfd8ba8c">3、</span><strong><span data-lake-id="uad0d15ab" id="uad0d15ab">堆是线程共享的</span></strong><span data-lake-id="uab25f6c9" id="uab25f6c9">，栈是线程独享的。</span></p>
  <p data-lake-id="u9e992246" id="u9e992246"><span data-lake-id="ue2924067" id="ue2924067">4、堆是垃圾回收的主要区域，不再引用这个对象，会被垃圾回收机制会自动回收。栈的内存使用是一种先进后出的机制，栈中的变量会在程序执行完毕后自动释放</span></p>
  <p data-lake-id="u65dcec03" id="u65dcec03"><span data-lake-id="u483ff2b6" id="u483ff2b6">5、栈的大小比堆要小的多，一般是几百到几千字节</span></p>
  <p data-lake-id="ue51d60d1" id="ue51d60d1"><span data-lake-id="u8a6b3895" id="u8a6b3895">6、栈的存储速度比堆快，代码执行效率高</span></p>
  <p data-lake-id="ucf463cb3" id="ucf463cb3"><span data-lake-id="u3d019ecf" id="u3d019ecf">7、堆上会发生OutofMemoryError，栈上会发生StackOverflowError</span></p>
  <p data-lake-id="u7598e36b" id="u7598e36b"><br></p>
  <h2 data-lake-id="XYQYu" id="XYQYu"><span data-lake-id="ud396dc03" id="ud396dc03">Java中的对象一定在堆上分配内存吗？</span></h2>
  <p data-lake-id="u0e424d10" id="u0e424d10"><br></p>
  <h2 data-lake-id="MldgV" id="MldgV"><span data-lake-id="u9b013eba" id="u9b013eba">什么是堆外内存？</span></h2>
  <p data-lake-id="u6c18f92e" id="u6c18f92e"><br></p>
  <p data-lake-id="u3972eab8" id="u3972eab8"><span data-lake-id="ue76d1231" id="ue76d1231">堆外内存是指将数据存储在堆以外的内存中，</span><strong><span data-lake-id="u90774a68" id="u90774a68">主要是指将一些直接内存分配在堆以外</span></strong><span data-lake-id="u9292988c" id="u9292988c">，以提高应用程序的性能或节省堆内存空间。堆外内存可以用于分配容量较大的缓冲区，比如文件缓冲区等等。</span></p>
  <p data-lake-id="u8d7dde8b" id="u8d7dde8b"><span data-lake-id="u123c5b30" id="u123c5b30">​</span><br></p>
  <p data-lake-id="u21278131" id="u21278131"><span data-lake-id="u57323bb6" id="u57323bb6">Java 中的直接内存是通过使用 java.nio 包中的 DirectByteBuffer 类来实现的。DirectByteBuffer 类可以用来分配本地内存，并允许在 Java 和本地内存之间交换数据。使用 DirectByteBuffer 可以减少从 Java 堆和本地堆之间进行复制和读写的开销，从而提升程序的性能。</span></p>
  <p data-lake-id="u971a5525" id="u971a5525"><span data-lake-id="udebaddcd" id="udebaddcd">​</span><br></p>
  <h2 data-lake-id="xHEFL" id="xHEFL"><span data-lake-id="u6a599bc4" id="u6a599bc4">方法区的变迁</span></h2>
  <p data-lake-id="u000b5a3e" id="u000b5a3e"><br></p>
  <p data-lake-id="u97bc4494" id="u97bc4494"><span data-lake-id="ue5a0bdd5" id="ue5a0bdd5">前面我们提过，方法区其实是JVM规范中定义出来的一块区域，具体的虚拟机实现上是有很大的差别的。在不同的版本中，方法区的位置也不尽相同。</span></p>
  <p data-lake-id="u5c7e26bc" id="u5c7e26bc"><br></p>
  <p data-lake-id="ube575db1" id="ube575db1"><br></p>
  <h2 data-lake-id="Wz75T" id="Wz75T"><br></h2>
  <p data-lake-id="u5b2603a3" id="u5b2603a3"><br></p>
 </body>
</html>